/**
* @file gui_message.c
* @brief message process
* @details 
* @author huangbin
* @version V1.0.0
* @date 2025-06-12
* @copyright All documents are copyrighted by Shenzhen Xinlongwei Technology Co., LTD.
*            They may not be used for commercial purposes without permission
*/

/*============================ INCLUDES ======================================*/

#define __GUI_MESSAGE_IMPLEMENT__
#include "./gui_message.h"

#if defined(__clang__)
//#   pragma clang diagnostic push
#   pragma clang diagnostic ignored "-Wunknown-warning-option"
#   pragma clang diagnostic ignored "-Wreserved-identifier"
#   pragma clang diagnostic ignored "-Wsign-conversion"
#   pragma clang diagnostic ignored "-Wpadded"
#   pragma clang diagnostic ignored "-Wcast-qual"
#   pragma clang diagnostic ignored "-Wcast-align"
#   pragma clang diagnostic ignored "-Wmissing-field-initializers"
#   pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#   pragma clang diagnostic ignored "-Wmissing-prototypes"
#   pragma clang diagnostic ignored "-Wunused-variable"
#   pragma clang diagnostic ignored "-Wunused-parameter"
#   pragma clang diagnostic ignored "-Wgnu-statement-expression"
#   pragma clang diagnostic ignored "-Wtautological-pointer-compare"
#elif __IS_COMPILER_ARM_COMPILER_5__
#elif __IS_COMPILER_GCC__
#   pragma GCC diagnostic push
#   pragma GCC diagnostic ignored "-Wformat="
#   pragma GCC diagnostic ignored "-Wpedantic"
#endif

/*============================ MACROS ========================================*/
#undef this
#define this    (*ptThis)
/*============================ MACROFIED FUNCTIONS ===========================*/

#define __MSG_NAME_STR(__MSG, __START)                  \
        [__MSG - __START] = #__MSG

/*============================ TYPES =========================================*/
/*============================ GLOBAL VARIABLES ==============================*/
/*============================ PROTOTYPES ====================================*/
static
void __gui_helper_run_key_frontend_critical_stage(gui_helper_msg_t *ptThis);

static
void __gui_helper_run_pointer_frontend_critical_stage(gui_helper_msg_t *ptThis);

/*============================ LOCAL VARIABLES ===============================*/

static
const char *c_chPointerMessageName[] = {
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_DOWN,            __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_HOLD,            __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_UP,              __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_CLICK,           __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_LONG_PRESSING,   __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_DOUBLE_CLICK,    __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_MOVE_IN,         __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_MOVE_OUT,        __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_HOVER,           __GUI_MSG_POINTER_EVT),
    __MSG_NAME_STR(GUI_MSG_POINTER_EVT_MOVE,            __GUI_MSG_POINTER_EVT),
};

static 
const char *c_chKeyMessageName[] = {
    __MSG_NAME_STR(GUI_MSG_KEY_EVT_DOWN,                __GUI_MSG_KEY_EVT),
    __MSG_NAME_STR(GUI_MSG_KEY_EVT_REPEAT,              __GUI_MSG_KEY_EVT),
    __MSG_NAME_STR(GUI_MSG_KEY_EVT_UP,                  __GUI_MSG_KEY_EVT),
    __MSG_NAME_STR(GUI_MSG_KEY_EVT_PRESSED,             __GUI_MSG_KEY_EVT),
    __MSG_NAME_STR(GUI_MSG_KEY_EVT_LONG_PRESSING,       __GUI_MSG_KEY_EVT),
    __MSG_NAME_STR(GUI_MSG_KEY_EVT_DOUBLE_PRESSED,      __GUI_MSG_KEY_EVT),
};

static 
const char *c_chGestureMessageName[] = {
    __MSG_NAME_STR(GUI_MSG_GESTURE_EVT_SLIDE,               __GUI_MSG_GESTURE_EVT),
    __MSG_NAME_STR(GUI_MSG_GESTURE_EVT_WHEEL,               __GUI_MSG_GESTURE_EVT),
    __MSG_NAME_STR(GUI_MSG_GESTURE_EVT_ZOOM_IN,             __GUI_MSG_GESTURE_EVT),
    __MSG_NAME_STR(GUI_MSG_GESTURE_EVT_ZOOM_OUT,            __GUI_MSG_GESTURE_EVT),
    __MSG_NAME_STR(GUI_MSG_GESTURE_EVT_ROTATE_CLOCKWISE,    __GUI_MSG_GESTURE_EVT),
    __MSG_NAME_STR(GUI_MSG_GESTURE_EVT_ROTATE_ANTICLOCKWISE,__GUI_MSG_GESTURE_EVT),
};

static 
const char *c_chWidgetsMessageName[] = {
    __MSG_NAME_STR(GUI_MSG_WIDGETS_EVT_ON_LOAD,             __GUI_MSG_WIDGETS_EVT),
    __MSG_NAME_STR(GUI_MSG_WIDGETS_EVT_ON_DEPOSE,           __GUI_MSG_WIDGETS_EVT),
    __MSG_NAME_STR(GUI_MSG_WIDGETS_EVT_UPDATE,              __GUI_MSG_WIDGETS_EVT),
    __MSG_NAME_STR(GUI_MSG_WIDGETS_EVT_UPDATE_TREE,         __GUI_MSG_WIDGETS_EVT),
    __MSG_NAME_STR(GUI_MSG_WIDGETS_EVT_REFRESH,             __GUI_MSG_WIDGETS_EVT),
    __MSG_NAME_STR(GUI_MSG_WIDGETS_EVT_POST_REFRESH,        __GUI_MSG_WIDGETS_EVT),
    __MSG_NAME_STR(GUI_MSG_WIDGETS_EVT_GET_ACTIVE,          __GUI_MSG_WIDGETS_EVT),
    __MSG_NAME_STR(GUI_MSG_WIDGETS_EVT_LOST_ACTIVE,         __GUI_MSG_WIDGETS_EVT),
};

/*============================ IMPLEMENTATION ================================*/

/*----------------------------------------------------------------------------*
 * Misc                                                                       *
 *----------------------------------------------------------------------------*/


ARM_NONNULL(1)
const char *gui_helper_msg_get_name(gui_msg_core_t *ptMSG)
{
    static const char c_cDefaultName[] = "UNKNOWN";

    if          (__GUI_MSG_WIDGETS_EVT == (ptMSG->u8ID & __GUI_MSG_WIDGETS_EVT_MSK)) {
        uint_fast8_t chIndex = ptMSG->u8ID - __GUI_MSG_WIDGETS_EVT;
        chIndex = MIN(dimof(c_chWidgetsMessageName), chIndex);
        
        return c_chWidgetsMessageName[chIndex];
    } else if   (__GUI_MSG_KEY_EVT == (ptMSG->u8ID & __GUI_MSG_KEY_EVT_MSK)) {
        uint_fast8_t chIndex = ptMSG->u8ID - __GUI_MSG_KEY_EVT;
        chIndex = MIN(dimof(c_chKeyMessageName), chIndex);
        
        return c_chKeyMessageName[chIndex];
    } else if   (__GUI_MSG_POINTER_EVT == (ptMSG->u8ID & __GUI_MSG_POINTER_EVT_MSK)) {
        uint_fast8_t chIndex = ptMSG->u8ID - __GUI_MSG_POINTER_EVT;
        chIndex = MIN(dimof(c_chPointerMessageName), chIndex);

        return c_chPointerMessageName[chIndex];
    } else if   (__GUI_MSG_GESTURE_EVT == (ptMSG->u8ID & __GUI_MSG_GESTURE_EVT_MSK)) {
        uint_fast8_t chIndex = ptMSG->u8ID - __GUI_MSG_GESTURE_EVT;
        chIndex = MIN(dimof(c_chGestureMessageName), chIndex);

        return c_chGestureMessageName[chIndex];
    } else if   (__GUI_MSG_WIDGETS_SPECIFIC_EVT == (ptMSG->u8ID & __GUI_MSG_WIDGETS_SPECIFIC_EVT_MSK)){
        static const char *c_cWidigetsSpecificEventName[] = {
            "GUI_MSG_WIDGETS_SPECIFIC_EVT"
        };

        uint_fast8_t chIndex = ptMSG->u8ID - __GUI_MSG_WIDGETS_SPECIFIC_EVT;
        chIndex = MIN(dimof(c_cWidigetsSpecificEventName), chIndex);

        return c_cWidigetsSpecificEventName[chIndex];
    }

    return c_cDefaultName;
}

ARM_NONNULL(1)
gui_msg_core_t *gui_helper_msg_item_init(gui_msg_core_t *ptItem)
{
    if (NULL != ptItem) {
        memset(ptItem, 0, sizeof(gui_msg_core_t));
        ptItem->u16Key = 0xBEEF;
    }

    return ptItem;
}

ARM_NONNULL(1)
gui_msg_core_t *gui_helper_msg_item_set_id(gui_msg_core_t *ptItem, uint8_t chID)
{
    if (NULL != ptItem) {
        ptItem->u8ID = chID;
    }

    return ptItem;
}

ARM_NONNULL(1)
uint_fast8_t gui_helper_msg_item_get_id(gui_msg_core_t *ptItem)
{
    uint8_t chID = GUI_MSG_INVALID;

    if (NULL != ptItem) {
        chID = ptItem->u8ID;
    }

    return chID;
}


/*----------------------------------------------------------------------------*
 * Message FIFO                                                               *
 *----------------------------------------------------------------------------*/

ARM_NONNULL(1)
gui_helper_msg_fifo_t *gui_helper_msg_fifo_init(gui_helper_msg_fifo_t *ptThis)
{
    assert(NULL != ptThis);

    memset(ptThis, 0, sizeof(gui_helper_msg_fifo_t));

    return ptThis;
}

ARM_NONNULL(1,2)
void gui_helper_msg_fifo_append(gui_helper_msg_fifo_t *ptThis, 
                                gui_msg_core_t *ptMSG)
{
    assert(NULL != ptThis);

    if (NULL == ptMSG) {
        return ;
    }

    arm_irq_safe {
        /* add the message to the FIFO */
        ARM_LIST_QUEUE_ENQUEUE(this.ptHead, this.ptTail, ptMSG);
    }
}

ARM_NONNULL(1)
bool gui_helper_msg_fifo_is_empty(gui_helper_msg_fifo_t *ptThis)
{
    assert(NULL != ptThis);

    return ARM_LIST_QUEUE_IS_EMPTY(this.ptHead, this.ptTail);
}



ARM_NONNULL(1)
gui_msg_core_t *gui_helper_msg_fifo_peek(gui_helper_msg_fifo_t *ptThis)
{
    assert(NULL != ptThis);

    gui_msg_core_t *ptMSG = NULL;

    arm_irq_safe {
        ARM_LIST_QUEUE_PEEK(this.ptHead, this.ptTail, ptMSG);
    };

    return ptMSG;
}

ARM_NONNULL(1)
gui_msg_core_t *gui_helper_msg_fifo_get(gui_helper_msg_fifo_t *ptThis)
{
    assert(NULL != ptThis);

    gui_msg_core_t *ptMSG = NULL;

    arm_irq_safe {
        ARM_LIST_QUEUE_DEQUEUE(this.ptHead, this.ptTail, ptMSG);
    };

    return ptMSG;
}


/*----------------------------------------------------------------------------*
 * Message POOL                                                               *
 *----------------------------------------------------------------------------*/

ARM_NONNULL(1)
gui_helper_msg_pool_t *gui_helper_msg_pool_init(gui_helper_msg_pool_t *ptThis,
                                                gui_helper_msg_pool_cfg_t *ptCFG)
{
    assert(NULL != ptThis);
    assert(NULL != ptCFG);

    memset(ptThis, 0, sizeof(gui_helper_msg_pool_t));
    this.tCFG = *ptCFG;

    return ptThis;
}

ARM_NONNULL(1,2)
void gui_helper_msg_pool_add_items( gui_helper_msg_pool_t *ptThis, 
                                    gui_msg_t *ptItems, 
                                    uint_fast16_t hwCount)
{
    assert(NULL != ptThis);

    if (NULL == ptItems || 0 == hwCount) {
        return ;
    }

    arm_foreach(gui_msg_t, ptItems, hwCount, ptItem) {

        gui_helper_msg_item_init((gui_msg_core_t *)ptItem);
        ptItem->use_as__gui_msg_core_t.bFromPool = true;

        gui_helper_msg_pool_free(ptThis, ptItem);
    }
}

ARM_NONNULL(1)
gui_msg_t *gui_helper_msg_pool_new(gui_helper_msg_pool_t *ptThis)
{
    assert(NULL != ptThis);

    gui_msg_t *ptItem = NULL;

    arm_irq_safe {
        if (NULL != this.ptFreeList) {
            ARM_LIST_STACK_POP(this.ptFreeList, ptItem);
        }
    }

    if (NULL != ptItem) {
        arm_with(gui_msg_core_t, (gui_msg_core_t *)ptItem) {
            _->u16Key = 0xBEEF;
            _->bFromPool = true;
            _->bFromHeap = false;
            _->ptNext = NULL;
        }

    #if GUI_CFG_LOG_MSG_POOL
        ARM_2D_LOG_INFO(
            GUI_STACK, 
            2, 
            "Message Pool", 
            "Allocate a new message item [%p] from the pool [%p]. ",
            ptItem,
            ptThis
        );
    #endif

    } else {
    #if GUI_CFG_LOG_MSG_POOL
        ARM_2D_LOG_WARNING(
            GUI_STACK, 
            2, 
            "Message Pool", 
            "Failed to allocate message, the pool [%p] is empty. ",
            ptThis
        );
    #endif

        if (this.tCFG.bAllowUsingHeap) {
            ptItem = (gui_msg_t *)__arm_2d_allocate_scratch_memory(
                                                            sizeof(gui_msg_t),
                                                            __alignof__(ptItem),
                                                            ARM_2D_MEM_TYPE_UNSPECIFIED);
            if (NULL != ptItem) {
             #if GUI_CFG_LOG_MSG_POOL
                ARM_2D_LOG_INFO(
                    GUI_STACK, 
                    2, 
                    "Message Pool", 
                    "Use heap as alternative, get one item [%p]",
                    ptItem
                );
            #endif

                arm_with(gui_msg_core_t, (gui_msg_core_t *)ptItem) {
                    _->u16Key = 0xBEEF;
                    _->bFromHeap = true;
                    _->bFromPool = false;
                    _->ptNext = NULL;
                }

            }
        }
    }

    return ptItem;
}

ARM_NONNULL(1)
gui_msg_core_t * gui_helper_msg_pool_free(gui_helper_msg_pool_t *ptThis, 
                                          gui_msg_t *ptItem)
{
    assert(NULL != ptThis);

    do {
        if (NULL == ptItem) {
            return NULL;
        } else if (ptItem->use_as__gui_msg_core_t.u16Key != 0xBEEF) {
            break;
        } else if (ptItem->use_as__gui_msg_core_t.bFromHeap) {
        #if GUI_CFG_LOG_MSG_POOL
            ARM_2D_LOG_INFO(
                GUI_STACK, 
                2, 
                "Message Pool", 
                "Free message item [%p] to the heap. ",
                ptItem
            );
        #endif
            __arm_2d_free_scratch_memory(ARM_2D_MEM_TYPE_UNSPECIFIED, ptItem);
            return NULL;
        } else if (!ptItem->use_as__gui_msg_core_t.bFromPool) {
            break;
        }

        /* clean it */
        memset(ptItem, 0, sizeof(gui_msg_t));

        /* put the item back to the pool */
        arm_irq_safe {
            __ARM_LIST_STACK_PUSH(this.ptFreeList, ptItem);
        }

    #if GUI_CFG_LOG_MSG_POOL
        ARM_2D_LOG_INFO(
            GUI_STACK, 
            2, 
            "Message Pool", 
            "Free message item [%p] to the pool [%p]. ",
            ptItem,
            ptThis
        );
    #endif

        /* nothing left for users */
        return NULL;
    } while(0);

#if GUI_CFG_LOG_MSG_POOL
    ARM_2D_LOG_INFO(
        GUI_STACK, 
        2, 
        "Message Pool", 
        "It message item [%p] is invalid or doesn't belong to the pool [%p]. ",
        ptItem,
        ptThis
    );
#endif

    /* users have to handle it themselves */
    return &ptItem->use_as__gui_msg_core_t;
}



/*----------------------------------------------------------------------------*
 * Message Helper                                                             *
 *----------------------------------------------------------------------------*/

ARM_NONNULL(1)
void gui_helper_msg_init(gui_helper_msg_t *ptThis, gui_helper_msg_cfg_t *ptCFG)
{
    assert(NULL != ptThis);
    assert(NULL != ptCFG);

    memset(ptThis, 0, sizeof(gui_helper_msg_t));

    this.tCFG = *ptCFG;

    do {
        gui_helper_msg_pool_cfg_t tCFG = {
            .bAllowUsingHeap = this.tCFG.bAllowUsingHeap,
        };

        gui_helper_msg_pool_init(&this.tPool, &tCFG);
    } while(0);

}

static
gui_msg_core_t *__gui_helper_msg_read_input_fifo(void *pObj)
{
    assert(NULL != pObj);
    gui_helper_msg_t *ptThis = (gui_helper_msg_t *)pObj;

    return gui_helper_msg_fifo_get(&this.MainPipe.tFIFO);
}

static
gui_msg_core_t *__gui_helper_msg_read_key_input_fifo(void *pObj)
{
    assert(NULL != pObj);
    gui_helper_msg_t *ptThis = (gui_helper_msg_t *)pObj;

    return gui_helper_msg_fifo_get(&this.KeyPipe.tFIFO);
}

static
gui_msg_core_t *__gui_helper_msg_read_pointer_input_fifo(void *pObj)
{
    assert(NULL != pObj);
    gui_helper_msg_t *ptThis = (gui_helper_msg_t *)pObj;

    return gui_helper_msg_fifo_get(&this.PointerPipe.tFIFO);
}

ARM_NONNULL(1, 2, 3)
static
bool ___gui_helper_msg_frontend_register(   gui_msg_frontend_chain_t *ptThis,
                                            gui_helper_msg_t *ptMSGCTRL,
                                            gui_i_msg_read_handler_t *fnRead,
                                            gui_msg_frontend_t *ptFrontend)
{
    assert(NULL != ptThis);
    assert(NULL != ptMSGCTRL);
    assert(NULL != ptFrontend);

    do {
        if (NULL == ptFrontend) {
            break;
        } else if (NULL == ptFrontend->fnProcess) {
            break;
        }

        gui_msg_frontend_t *ptLast = NULL;

        /* get the last frontend in the chain */
        ARM_LIST_QUEUE_PEEK(this.ptHead, 
                            this.ptTail,
                            ptLast);
        
        if (NULL == ptLast) {
            /* the frontend chain is empty, connect the new frontend to the intput fifo */
            ptFrontend->Dependency.tIn.fnRead = fnRead;
            ptFrontend->Dependency.tIn.pTarget = (void *)ptMSGCTRL;
        } else {

            /* connect to the last frontend */
            ptFrontend->Dependency.tIn.fnRead = ptLast->ptChain->fnRead;
            ptFrontend->Dependency.tIn.pTarget = (void *)ptLast;
        }
        ptFrontend->ptPool = &ptMSGCTRL->tPool;

        /* add frontend to the chain */
        ARM_LIST_QUEUE_ENQUEUE( this.ptHead, 
                                this.ptTail,
                                ptFrontend);

        return true;
    } while(0);

    return false;
}

ARM_NONNULL(1, 2)
bool gui_helper_msg_frontend_register(  gui_helper_msg_t *ptThis, 
                                        gui_msg_frontend_t *ptFrontend,
                                        gui_msg_frontend_type_t tType)
{
    assert(NULL != ptThis);
    assert(NULL != ptFrontend);

    switch (tType) {
        case GUI_FRONTEND_KEY:
            return ___gui_helper_msg_frontend_register( &this.KeyPipe.tChain, 
                                                        ptThis,
                                                        &__gui_helper_msg_read_key_input_fifo,
                                                        ptFrontend);

        case GUI_FRONTEND_POINTER:
            return ___gui_helper_msg_frontend_register( &this.PointerPipe.tChain, 
                                                        ptThis,
                                                        &__gui_helper_msg_read_pointer_input_fifo,
                                                        ptFrontend);

        case GUI_FRONTEND_GENERIC:
        default:
            return ___gui_helper_msg_frontend_register( &this.MainPipe.tChain, 
                                                        ptThis,
                                                        &__gui_helper_msg_read_input_fifo,
                                                        ptFrontend);
    }

    
}


static
void __gui_helper_run_key_frontend_critical_stage(gui_helper_msg_t *ptThis)
{
    if (NULL == this.KeyPipe.tChain.ptHead) {
        return ;
    }

    do {
        arm_fsm_rt_t tResult = 
            ARM_2D_INVOKE(this.KeyPipe.tChain.ptHead->fnProcess,
                ARM_2D_PARAM(
                    this.KeyPipe.tChain.ptHead,
                    GUI_FRONTEND_PROCESS_MODE_CRITICAL
                ));
        if (arm_fsm_rt_cpl == tResult || arm_fsm_rt_wait_for_obj == tResult) {
            break;
        }
    } while(1);
}

static
void __gui_helper_run_pointer_frontend_critical_stage(gui_helper_msg_t *ptThis)
{
    if (NULL == this.PointerPipe.tChain.ptHead) {
        return ;
    }

    do {
        arm_fsm_rt_t tResult = 
            ARM_2D_INVOKE(this.PointerPipe.tChain.ptHead->fnProcess,
                ARM_2D_PARAM(
                    this.PointerPipe.tChain.ptHead,
                    GUI_FRONTEND_PROCESS_MODE_CRITICAL
                ));
        if (arm_fsm_rt_cpl == tResult || arm_fsm_rt_wait_for_obj == tResult) {
            break;
        }
    } while(1);
}


ARM_NONNULL(1)
arm_fsm_rt_t gui_helper_msg_task(gui_helper_msg_t *ptThis)
{
    assert(NULL != ptThis);
    arm_fsm_rt_t tResult;

    if (!this.bIsWorking) {
        return arm_fsm_rt_cpl;
    }

ARM_PT_BEGIN(this.chPT)

    this.bIsComplete = true;
    this.bFIFOEmpty = true;

    /* handle key pipe frontend(s) */
    this.KeyPipe.tChain.ptCurrent = this.KeyPipe.tChain.ptHead;
    
    while(NULL != this.KeyPipe.tChain.ptCurrent) {

        do {
            if (NULL == this.KeyPipe.tChain.ptCurrent->fnProcess) {
                break;
            }

            if (this.KeyPipe.tChain.ptCurrent == this.KeyPipe.tChain.ptHead) {
                tResult = this.KeyPipe.tChain.ptCurrent->fnProcess(
                                                    this.KeyPipe.tChain.ptCurrent,
                                                    GUI_FRONTEND_PROCESS_MODE_NON_CRITICAL);
            } else {
                tResult = this.KeyPipe.tChain.ptCurrent->fnProcess(
                                                    this.KeyPipe.tChain.ptCurrent,
                                                    GUI_FRONTEND_PROCESS_MODE_ALL);
            }

            if (arm_fsm_rt_cpl != tResult && arm_fsm_rt_wait_for_obj != tResult) {
                this.bIsComplete = false;
            }
            if (tResult != arm_fsm_rt_cpl) {
                this.bFIFOEmpty = false;
            }

        ARM_PT_YIELD(arm_fsm_rt_on_going);

        } while(0);

        /* get next frontend */
        this.KeyPipe.tChain.ptCurrent = this.KeyPipe.tChain.ptCurrent->ptNext;
    }

    if (NULL != this.KeyPipe.tChain.ptTail) {
        do {
            gui_msg_core_t *ptItem = gui_helper_msg_fifo_get(&this.KeyPipe.tChain.ptTail->tOutputFIFO);
            if (NULL == ptItem) {
                break;
            }

            gui_helper_msg_fifo_append(&this.MainPipe.tFIFO, ptItem);
        } while(1);
    }


    /* handle pointer pipe frontend(s) */
    this.PointerPipe.tChain.ptCurrent = this.PointerPipe.tChain.ptHead;
    while(NULL != this.PointerPipe.tChain.ptCurrent) {

        do {
            if (NULL == this.PointerPipe.tChain.ptCurrent->fnProcess) {
                break;
            }

            if (this.PointerPipe.tChain.ptCurrent == this.PointerPipe.tChain.ptHead) {
                tResult = this.PointerPipe.tChain.ptCurrent->fnProcess(
                                                    this.PointerPipe.tChain.ptCurrent,
                                                    GUI_FRONTEND_PROCESS_MODE_NON_CRITICAL);
            } else {
                tResult = this.PointerPipe.tChain.ptCurrent->fnProcess(
                                                    this.PointerPipe.tChain.ptCurrent,
                                                    GUI_FRONTEND_PROCESS_MODE_ALL);
            }

            if (arm_fsm_rt_cpl != tResult && arm_fsm_rt_wait_for_obj != tResult) {
                this.bIsComplete = false;
            }
            if (tResult != arm_fsm_rt_cpl) {
                this.bFIFOEmpty = false;
            }

        ARM_PT_YIELD(arm_fsm_rt_on_going);

        } while(0);

        /* get next frontend */
        this.PointerPipe.tChain.ptCurrent = this.PointerPipe.tChain.ptCurrent->ptNext;
    }

    if (NULL != this.PointerPipe.tChain.ptTail) {
        do {
            gui_msg_core_t *ptItem = gui_helper_msg_fifo_get(&this.PointerPipe.tChain.ptTail->tOutputFIFO);
            if (NULL == ptItem) {
                break;
            }
            gui_helper_msg_fifo_append(&this.MainPipe.tFIFO, ptItem);
        } while(1);
    }


    /* handle the main pipe */
    this.MainPipe.tChain.ptCurrent = this.MainPipe.tChain.ptHead;
    while(NULL != this.MainPipe.tChain.ptCurrent) {

        do {
            if (NULL == this.MainPipe.tChain.ptCurrent->fnProcess) {
                break;
            }

            tResult = this.MainPipe.tChain.ptCurrent->fnProcess(
                                                this.MainPipe.tChain.ptCurrent,
                                                GUI_FRONTEND_PROCESS_MODE_ALL);

            if (arm_fsm_rt_cpl != tResult && arm_fsm_rt_wait_for_obj != tResult) {
                this.bIsComplete = false;
            }
            if (tResult != arm_fsm_rt_cpl) {
                this.bFIFOEmpty = false;
            }

        ARM_PT_YIELD(arm_fsm_rt_on_going);

        } while(0);

        /* get next frontend */
        this.MainPipe.tChain.ptCurrent = this.MainPipe.tChain.ptCurrent->ptNext;
    }

ARM_PT_END()

    if (this.bFIFOEmpty) {
        if (this.bIsWorking) {
            ARM_2D_LOG_INFO (
                GUI_STACK,
                1,
                "Message Helper",
                "Message Pipeline enters idle state."
            );
        }

        arm_irq_safe {
            this.bIsWorking = false;
        }
    }

    return this.bIsComplete ? arm_fsm_rt_cpl : arm_fsm_rt_on_going;
}


ARM_NONNULL(1,2)
void gui_helper_msg_add_items_to_pool(  gui_helper_msg_t *ptThis, 
                                        gui_msg_t *ptItems, 
                                        uint_fast16_t hwCount)
{
    assert(NULL != ptThis);

    gui_helper_msg_pool_add_items(&this.tPool, ptItems, hwCount);
}


ARM_NONNULL(1)
gui_msg_core_t * gui_helper_msg_free(gui_helper_msg_t *ptThis, gui_msg_t *ptItem)
{
    assert(NULL != ptThis);

    return gui_helper_msg_pool_free(&this.tPool, ptItem);
}


ARM_NONNULL(1)
gui_msg_t *gui_helper_msg_new(gui_helper_msg_t *ptThis)
{
    assert(NULL != ptThis);

    return gui_helper_msg_pool_new(&this.tPool);
}

ARM_NONNULL(1,2)
void gui_helper_msg_send(gui_helper_msg_t *ptThis, gui_msg_core_t *ptMSG) 
{
    assert(NULL != ptThis);

    if ((__GUI_MSG_KEY_EVT == (ptMSG->u8ID & __GUI_MSG_KEY_EVT_MSK))
    &&  (NULL != this.KeyPipe.tChain.ptHead)) {

        gui_helper_msg_fifo_append(&this.KeyPipe.tFIFO, ptMSG);
        __gui_helper_run_key_frontend_critical_stage(ptThis);

    } else if ((__GUI_MSG_POINTER_EVT == (ptMSG->u8ID & __GUI_MSG_POINTER_EVT_MSK))
           &&  (NULL != this.PointerPipe.tChain.ptHead)) {

        gui_helper_msg_fifo_append(&this.PointerPipe.tFIFO, ptMSG);

        __gui_helper_run_pointer_frontend_critical_stage(ptThis);
    } else {

        gui_helper_msg_fifo_append(&this.MainPipe.tFIFO, ptMSG);
    }

    if (!this.bIsWorking) {
        ARM_2D_LOG_INFO (
            GUI_STACK,
            1,
            "Message Helper",
            "Message Pipeline starts working..."
        );
    }

    arm_irq_safe {
        this.bIsWorking = true;
    }
    
}

ARM_NONNULL(1)
gui_msg_core_t *gui_helper_msg_peek(gui_helper_msg_t *ptThis)
{
    assert(NULL != ptThis);
    gui_msg_core_t *ptItem = NULL;

    do {
        if (NULL == this.MainPipe.tChain.ptHead) {
            ptItem = gui_helper_msg_fifo_peek(&this.MainPipe.tFIFO);
            break;
        }

        assert(this.MainPipe.tChain.ptTail != NULL);
        ptItem = gui_helper_msg_fifo_peek(&this.MainPipe.tChain.ptTail->tOutputFIFO);

    } while(0);


#if GUI_CFG_LOG_MSG_OUT
    if (NULL != ptItem) {
        do {
        #if !GUI_CFG_LOG_MSG_IO_KEY_EVT
            if (__GUI_MSG_KEY_EVT == (ptItem->u8ID & __GUI_MSG_KEY_EVT_MSK)) {
                break;
            }
        #endif

        #if !GUI_CFG_LOG_MSG_IO_POINTER_EVT
            if (__GUI_MSG_POINTER_EVT == (ptItem->u8ID & __GUI_MSG_POINTER_EVT_MSK)) {
                break;
            }
        #endif
            ARM_2D_LOG_INFO(
                GUI_STACK, 
                2, 
                "Message Helper", 
                "Peek a message from helper [%p]: [ID: 0x%02x][Target: %p][%s]",
                ptThis,
                ptItem->u8ID,
                ptItem->ptTargetWidget,
                gui_helper_msg_get_name(ptItem)
            );
        } while(0);
    }
#endif

    return ptItem;
}

ARM_NONNULL(1)
gui_msg_core_t *gui_helper_msg_get(gui_helper_msg_t *ptThis)
{
    assert(NULL != ptThis);
    gui_msg_core_t *ptItem = NULL;

    do {
        if (NULL == this.MainPipe.tChain.ptHead) {
            ptItem = gui_helper_msg_fifo_get(&this.MainPipe.tFIFO);
            break;
        }

        assert(this.MainPipe.tChain.ptTail != NULL);
        ptItem = gui_helper_msg_fifo_get(&this.MainPipe.tChain.ptTail->tOutputFIFO);

    } while(0);


#if GUI_CFG_LOG_MSG_OUT
    if (NULL != ptItem) {
        do {
        #if !GUI_CFG_LOG_MSG_IO_KEY_EVT
            if (__GUI_MSG_KEY_EVT == (ptItem->u8ID & __GUI_MSG_KEY_EVT_MSK)) {
                break;
            }
        #endif

        #if !GUI_CFG_LOG_MSG_IO_POINTER_EVT
            if (__GUI_MSG_POINTER_EVT == (ptItem->u8ID & __GUI_MSG_POINTER_EVT_MSK)) {
                break;
            }
        #endif

            ARM_2D_LOG_INFO(
                GUI_STACK, 
                2, 
                "Message Helper", 
                "Fetch a message from helper [%p]: [ID: 0x%02x][Target: %p][%s]",
                ptThis,
                ptItem->u8ID,
                ptItem->ptTargetWidget,
                gui_helper_msg_get_name(ptItem)
            );
        } while(0);
    }
#endif

    return ptItem;
}

/*----------------------------------------------------------------------------*
 * User Message API                                                           *
 *----------------------------------------------------------------------------*/

ARM_NONNULL(1)
bool gui_helper_msg_send_key_event(gui_helper_msg_t *ptThis,
                                   uint8_t chKeyEvent,
                                   uint16_t hwKeyValue,
                                   void *pTargetWidget)
{
    do {
        /* not a key event */
        if (__GUI_MSG_KEY_EVT != (chKeyEvent & __GUI_MSG_KEY_EVT_MSK)) {
            break;
        } 
        /* maybe sending GUI_KEY_NONE means something in your application
        else if (wKeyValue == GUI_KEY_NONE) {
            break;
        }
        */

        gui_msg_key_evt_t *ptKeyMSG = (gui_msg_key_evt_t *)gui_helper_msg_new(ptThis);
        if (NULL == ptKeyMSG) {
            break;
        }

        ptKeyMSG->hwKeyValue = hwKeyValue;
        ptKeyMSG->use_as__gui_msg_core_t.ptTargetWidget = pTargetWidget;
        ptKeyMSG->use_as__gui_msg_core_t.u8ID = chKeyEvent;

    #if GUI_CFG_LOG_MSG_IO_KEY_EVT && GUI_CFG_LOG_MSG_IN
        ARM_2D_LOG_INFO(
            GUI_STACK, 
            1, 
            "Message Input", 
            "[%s][ID: 0x%02x][Key: 0x%04x]",
            gui_helper_msg_get_name((gui_msg_core_t *)ptKeyMSG),
            chKeyEvent,
            hwKeyValue
        );
    #endif

        /* send message */
        gui_helper_msg_send(ptThis, &ptKeyMSG->use_as__gui_msg_core_t);

        return true;
    } while(0);

    return false;
}

ARM_NONNULL(1)
bool gui_helper_msg_send_pointer_event( gui_helper_msg_t *ptThis,
                                        uint8_t chPointerEvent,
                                        uint16_t hwPointerIndex,
                                        int16_t iX,
                                        int16_t iY,
                                        void *pTargetWidget)
{
    do {
        /* not a pointer event */
        if (__GUI_MSG_POINTER_EVT 
        != (chPointerEvent & __GUI_MSG_POINTER_EVT_MSK)) {
            break;
        }

        gui_msg_pointer_evt_t *ptPointerMSG 
            = (gui_msg_pointer_evt_t *)gui_helper_msg_new(ptThis);
        if (NULL == ptPointerMSG) {
            break;
        }
        
        arm_with(gui_msg_pointer_evt_t, ptPointerMSG) {
            _->hwIndex = hwPointerIndex;
            _->use_as__arm_2d_location_t.iX = iX;
            _->use_as__arm_2d_location_t.iY = iY;
            _->use_as__gui_msg_core_t.ptTargetWidget = pTargetWidget;
            _->use_as__gui_msg_core_t.u8ID = chPointerEvent;
        };

    #if GUI_CFG_LOG_MSG_IO_POINTER_EVT && GUI_CFG_LOG_MSG_IN
        ARM_2D_LOG_INFO(
            GUI_STACK, 
            1, 
            "Message Input", 
            "[%s][ID: 0x%02x][Index: 0x%04x][Offset: (%"PRIi16",%"PRIi16")]",
            gui_helper_msg_get_name((gui_msg_core_t *)ptPointerMSG),
            chPointerEvent,
            hwPointerIndex,
            iX,
            iY
        );
    #endif

        /* send message */
        gui_helper_msg_send(ptThis, &ptPointerMSG->use_as__gui_msg_core_t);

        return true;
    } while(0);

    return false;
}


